// ue4 render
let resizeTimeout;
const WS_OPEN_STATE = 1;
let selfRender;
let mouseDownBtnsFlag = {};
function sendInputData(data) {
    if (selfRender.webRtcPlayerObj) {
        selfRender.webRtcPlayerObj.send(data);
    }
}
const SpecialKeyCodes = {
    BackSpace: 8,
    Shift: 16,
    Control: 17,
    Alt: 18,
    RightShift: 253,
    RightControl: 254,
    RightAlt: 255,
    F11: 122
};

function getKeyCode(e) {
    if (e.keyCode === SpecialKeyCodes.Shift && e.code === 'ShiftRight') return SpecialKeyCodes.RightShift;
    else if (e.keyCode === SpecialKeyCodes.Control && e.code === 'ControlRight') return SpecialKeyCodes.RightControl;
    else if (e.keyCode === SpecialKeyCodes.Alt && e.code === 'AltRight') return SpecialKeyCodes.RightAlt;
    else return e.keyCode;
}

const LoadStateType = {
    Waiting: 1,
    Connecting: 2,
    Connected: 3
};

const ToClientMessageType = {
    QualityControlOwnership: 0,
    Response: 1,
    Command: 2,
    FreezeFrame: 3,
    UnfreezeFrame: 4
};

const MessageType = {

    /**********************************************************************/

    /*
     * Control Messages. Range = 0..49.
     */
    IFrameRequest: 0,
    RequestQualityControl: 1,
    MaxFpsRequest: 2,
    AverageBitrateRequest: 3,
    StartStreaming: 4,
    StopStreaming: 5,

    /**********************************************************************/

    /*
     * Input Messages. Range = 50..89.
     */

    // Generic Input Messages. Range = 50..59.
    UIInteraction: 50,
    Command: 51,

    String: 52,
    OpenLevel: 53,

    // Keyboard Input Message. Range = 60..69.
    KeyDown: 60,
    KeyUp: 61,
    KeyPress: 62,

    // Mouse Input Messages. Range = 70..79.
    MouseEnter: 70,
    MouseLeave: 71,
    MouseDown: 72,
    MouseUp: 73,
    MouseMove: 74,
    MouseWheel: 75,
    MouseDrag: 76,
    MouseClick: 77,

    // Touch Input Messages. Range = 80..89.
    TouchStart: 80,
    TouchEnd: 81,
    TouchMove: 82

    /**************************************************************************/
};

let playerElementClientRect = undefined;
let normalizeAndQuantizeUnsigned = undefined;
let normalizeAndQuantizeSigned = undefined;

function UERender(options) {
    let self = this;
    selfRender = this;
    self.ws = undefined;
    self.isPlaying = false;
    this.callbacks = {};
    this.sequence = 1000;
    if (options.level===undefined)options.level=4;
    if (options.pick_mouse===undefined)options.pick_mouse=0;
    this.options = options || {};
    this.pickable = this.options.pickable;
    this.onPick = this.options.onPick;
    this.onCustomData = this.options.onCustomData;
    this.onLabelClick = this.options.onLabelClick;
    self.print_inputs = this.options.print_inputs;
    this.startPlay = function () {
        self.lastTimeResized = new Date().getTime();
        self.playerElement = document.getElementById(self.options.elementId);
        window.addEventListener('resize', resizePlayer, true);
        self.playerElement.addEventListener("mousewheel", onMousewheel, false);
        self.playerElement.addEventListener("mousedown", onMouseDown, false);
        self.playerElement.addEventListener("mousemove", onMouseMove, false);
        self.playerElement.addEventListener("mouseup", onMouseUp, false);
        self.playerElement.setAttribute('tabindex','0');
        self.playerElement.setAttribute('onblur','onPlayerBlur()');
        document.addEventListener("keydown", onKeydown, false);
        document.addEventListener("keypress", onKeypress, false);
        document.addEventListener("keyup", onKeyup, false);
        document.oncontextmenu = function(){return false;};
        start();
    };
    
    this.custom = function (code,data,callback) {
        let p = {
            code: code,
            data: data
        };
        self.call(9999,p,callback);
    };

    this.call = function (funCode,param,callback) {
        let _fid = self.sequence.toString();
        if(param === undefined){
            param = true;
        }
        let m = {type:'fun',fid:_fid,type0:2,type1:funCode,data:param};
        self.sequence = self.sequence+1;
        if (callback){
            self.callbacks[_fid] = callback;
        }
        self.ws.send(JSON.stringify(m));
    };

    this.callMulti = function (params,callback) {
        let _fid = self.sequence.toString();
        if(params === undefined){
            params = false;
        }
        let m = {type:'fun',fid:_fid,type0:2,datas:params};
        self.sequence = self.sequence+1;
        if (callback){
            self.callbacks[_fid] = callback;
        }
        self.ws.send(JSON.stringify(m));
    };

    this.close = function() {
        self.useStart = false;
        if (self.ws){
            self.ws.close();
        }
    };

    function start(){
        "use strict";
        self.useStart = true;
        window.WebSocket = window.WebSocket || window.MozWebSocket;
        if (!window.WebSocket) {
            alert('Your browser doesn\'t support WebSocket');
            return;
        }
        if (!self.ws) {
            self.wsurl = 'ws://' + self.options.serverHost + '?player=1&name='+self.options.ue4name;
            console.log('开始连接'+self.wsurl);
            let ws = new WebSocket(self.wsurl);
            self.ws = ws;
            ws.onmessage = function (event) {
                if (self.print_inputs){
                    console.log(`<- SS: ${event.data}`);
                }
                let msg = JSON.parse(event.data);
                if (msg.type === 'config') {
                    onConfig(msg);
                }  else if (msg.type === 'answer') {
                    onWebRtcAnswer(msg);
                    setTimeout(onReadyPlay,1000);
                } else if (msg.type === 'iceCandidate') {
                    onWebRtcIce(msg.candidate);
                } else if (msg.type === 'fun') {
                    if(msg.type0 === 1){ //系统消息
                        if(self.options.onLoadStateChange){
                            self.options.onLoadStateChange(msg.type1);
                        }
                    }else if (msg.type0 === 2){//函数回调
                        if (msg.fid === 'LabelClick'){
                            if (self.onLabelClick){
                                let d = {};
                                if (msg.data)d=JSON.parse(msg.data);
                                self.onLabelClick(d);
                            }
                        }else if (msg.fid === 'event') {
                            if(self.onCustomData){
                                let d = {};
                                if (msg.data)d=JSON.parse(msg.data);
                                self.onCustomData(d);
                            }
                        } else{
                            let b = self.callbacks[msg.fid];
                            if (b){
                                let r = {};
                                if (msg.data)r =  JSON.parse(msg.data);
                                b(r);
                                delete self.callbacks[msg.fid];
                            }
                        }
                    }
                }else {
                    console.log(`invalid SS message type: ${msg.type}`);
                }
            };

            ws.onopen = function (event) {
                // body...
            };

            ws.onerror = function (event) {
                console.log(`WS error: ${JSON.stringify(event)}`);
            };

            ws.onclose = function (event) {
                console.log(`WS closed: ${JSON.stringify(event.code)} - ${event.wasClean}`);
                self.ws = undefined;
                self.is_reconnection = true;

                // destroy `webRtcPlayerObj` if any
                let playerDiv = self.playerElement;
                if (self.webRtcPlayerObj) {
                    if (playerDiv) playerDiv.removeChild(self.webRtcPlayerObj.video);
                    self.webRtcPlayerObj.close();
                    self.webRtcPlayerObj = undefined;
                }
                if (self.useStart) {
                    start();
                }
            };
        }
    }

    function onConfig(config) {
        console.log('ws debug onconfig');
        let playerDiv = self.playerElement;
        setupWebRtcPlayer(playerDiv, config);
        resizePlayer();
    }

    function onWebRtcIce(iceCandidate) {
        if (self.webRtcPlayerObj)
            self.webRtcPlayerObj.handleCandidateFromServer(iceCandidate);
    }

    function emitDescriptor(messageType, descriptor) {
        let descriptorAsString = JSON.stringify(descriptor);
        // Add the UTF-16 JSON string to the array byte buffer, going two bytes at
        // a time.
        let data = new DataView(new ArrayBuffer(1 + 2 + 2 * descriptorAsString.length));
        let byteIdx = 0;
        data.setUint8(byteIdx, messageType);
        byteIdx++;
        data.setUint16(byteIdx, descriptorAsString.length, true);
        byteIdx += 2;
        for (let i = 0; i < descriptorAsString.length; i++) {
            data.setUint16(byteIdx, descriptorAsString.charCodeAt(i), true);
            byteIdx += 2;
        }
        if (self.options.print_inputs){
            console.log('send emitDescriptor,'+descriptorAsString);
        }
        sendInputData(data.buffer);
    }

    this.emitUIInteraction = function(descriptor) {
        emitDescriptor(MessageType.UIInteraction, descriptor);
    };
    this.setStreamSize = function(sizeX,sizeY){
        let descriptor = {
            'resize': {'width':sizeX,'height':sizeY}
        };
        self.emitUIInteraction(descriptor);
    };

    function updateVideoSize() {
        let playerElement = self.playerElement;
        playerElementClientRect = playerElement.getBoundingClientRect();
        let now = new Date().getTime();
        if (now - self.lastTimeResized > 100) {
            if (!playerElement)
                return;
            let zoom = document.body.style.zoom;
            if(!zoom)zoom = 1;
            console.log('curr zoom:',zoom);
            let descriptor = {
                'resize': {'width':playerElement.clientWidth*zoom,'height':playerElement.clientHeight*zoom}
            };
            self.emitUIInteraction(descriptor);
            self.lastTimeResized = new Date().getTime();
        } else {
            console.log('Resizing too often - skipping');
            clearTimeout(resizeTimeout);
            resizeTimeout = setTimeout(resizePlayer, 1000);
        }
    }

    function resizePlayer() {
        updateVideoSize();
        setupNormalizeAndQuantize();
        //resizePlayerStyleToFillWindow();
    }

    function onReadyPlay() {
        self.webRtcPlayerObj.video.play();
        requestQualityControl();
        if(self.options.onLoadComplete){
            self.options.onLoadComplete();
        }
        self.isPlaying = true;
    }

    function sendWsMessage(msg) {
        let sz = msg.length;
        const max = 5000;
        let msgs = [];
        if (sz > max){
            let count = Math.ceil(sz/max);
            let sign = '%STR%';
            for(let x=0;x<count;x++){
                if (x===(count-1))sign='%END%';
                msgs.push(sign+msg.substr(x*max,Math.min(max, sz-max*x)));
            }
        }else{
            msgs.push(msg);
        }
        for (let i=0;i<msgs.length;i++){
            self.ws.send(msgs[i]);
        }
    }

    function setupWebRtcPlayer(htmlElement, config) {
        let webRtcPlayerObj = new webRtcPlayer({peerConnectionOptions: config.peerConnectionOptions});
        self.webRtcPlayerObj = webRtcPlayerObj;
        htmlElement.appendChild(webRtcPlayerObj.video);
        let ws = self.ws;
        webRtcPlayerObj.onWebRtcOffer = function (offer) {
            console.log(`ws debug onWebRtcOffer: ${ws.readyState}`);
            if (ws && ws.readyState === WS_OPEN_STATE) {
                let offerStr = JSON.stringify(offer);
                console.log(`-> SS: offer:\n${offerStr.length}`);
                sendWsMessage(offerStr);
            }
        };

        webRtcPlayerObj.onWebRtcCandidate = function (candidate) {
            console.log(`ws debug onWebRtcCandidate: ${ws.readyState}`);
            if (ws && ws.readyState === WS_OPEN_STATE) {
                console.log(`-> SS: iceCandidate\n${JSON.stringify(candidate, undefined, 4)}`);
                sendWsMessage(JSON.stringify({type: 'iceCandidate', candidate: candidate}));
            }
        };

        webRtcPlayerObj.onVideoInitialised = function () {
            if (ws && ws.readyState === WS_OPEN_STATE) {
                resizePlayer();
            }
        };

        webRtcPlayerObj.onDataChannelConnected = function () {
            if (ws && ws.readyState === WS_OPEN_STATE) {
                //showTextOverlay('WebRTC connected, waiting for video');
            }
        };

        webRtcPlayerObj.onDataChannelMessage = function (data) {
            var view = new Uint8Array(data);
            if (view[0] === ToClientMessageType.QualityControlOwnership) {
                let ownership = view[1] === 0 ? false : true;
                // If we own the quality control, we can't relenquish it. We only loose
                // quality control when another peer asks for it

            } else if (view[0] === ToClientMessageType.Response) {
                let response = new TextDecoder("utf-16").decode(data.slice(1));
                for (let listener of responseEventListeners.values()) {
                    listener(response);
                }
            } else if (view[0] === ToClientMessageType.Command) {
                let commandAsString = new TextDecoder("utf-16").decode(data.slice(1));
                console.log(commandAsString);
                let command = JSON.parse(commandAsString);
                if (command.command === 'onScreenKeyboard') {
                    //showOnScreenKeyboard(command);
                }
            }  else {
                console.error(`unrecognised data received, packet ID ${view[0]}`);
            }
        };
        //registerInputs(webRtcPlayerObj.video);
        createWebRtcOffer();
        console.log(`ws debug onWebRtcOffer1: ${ws.readyState}`);
        return webRtcPlayerObj.video;
    }

    function createWebRtcOffer() {
        if (self.webRtcPlayerObj) {
            console.log('Creating offer');
            self.webRtcPlayerObj.createOffer();
        } else {
            console.log('WebRTC player not setup, cannot create offer');
        }
    }

    function onWebRtcAnswer(webRTCData) {
        let webRtcPlayerObj = self.webRtcPlayerObj;
        webRtcPlayerObj.receiveAnswer(webRTCData);

        let printInterval = 5 * 60 * 1000; /*Print every 5 minutes*/
        let nextPrintDuration = printInterval;

        webRtcPlayerObj.onAggregatedStats = (aggregatedStats) =>
        {
            let numberFormat = new Intl.NumberFormat(window.navigator.language, {maximumFractionDigits: 0});
            let timeFormat = new Intl.NumberFormat(window.navigator.language, {
                maximumFractionDigits: 0,
                minimumIntegerDigits: 2
            });
            let statsText = '';

            // Calculate duration of run
            let runTime = (aggregatedStats.timestamp - aggregatedStats.timestampStart) / 1000;
            let timeValues = [];
            let timeDurations = [60, 60];
            for (let timeIndex = 0; timeIndex < timeDurations.length; timeIndex++) {
                timeValues.push(runTime % timeDurations[timeIndex]);
                runTime = runTime / timeDurations[timeIndex];
            }
            timeValues.push(runTime);

            let runTimeSeconds = timeValues[0];
            let runTimeMinutes = Math.floor(timeValues[1]);
            let runTimeHours = Math.floor([timeValues[2]]);

            let receivedBytesMeasurement = 'B';
            let receivedBytes = aggregatedStats.hasOwnProperty('bytesReceived') ? aggregatedStats.bytesReceived : 0;
            let dataMeasurements = ['kB', 'MB', 'GB'];
            for (let index = 0; index < dataMeasurements.length; index++) {
                if (receivedBytes < 100 * 1000)
                    break;
                receivedBytes = receivedBytes / 1000;
                receivedBytesMeasurement = dataMeasurements[index];
            }

            statsText += `Duration: ${timeFormat.format(runTimeHours)}:${timeFormat.format(runTimeMinutes)}:${timeFormat.format(runTimeSeconds)}</br>`;
            statsText += `Video Resolution: ${
                aggregatedStats.hasOwnProperty('frameWidth') && aggregatedStats.frameWidth && aggregatedStats.hasOwnProperty('frameHeight') && aggregatedStats.frameHeight ?
                    aggregatedStats.frameWidth + 'x' + aggregatedStats.frameHeight : 'N/A'
                }</br>`;
            statsText += `Received (${receivedBytesMeasurement}): ${numberFormat.format(receivedBytes)}</br>`;
            statsText += `Frames Decoded: ${aggregatedStats.hasOwnProperty('framesDecoded') ? numberFormat.format(aggregatedStats.framesDecoded) : 'N/A'}</br>`;
            statsText += `Packets Lost: ${aggregatedStats.hasOwnProperty('packetsLost') ? numberFormat.format(aggregatedStats.packetsLost) : 'N/A'}</br>`;
            statsText += `Bitrate (kbps): ${aggregatedStats.hasOwnProperty('bitrate') ? numberFormat.format(aggregatedStats.bitrate) : 'N/A'}</br>`;
            statsText += `Framerate: ${aggregatedStats.hasOwnProperty('framerate') ? numberFormat.format(aggregatedStats.framerate) : 'N/A'}</br>`;
            statsText += `Frames dropped: ${aggregatedStats.hasOwnProperty('framesDropped') ? numberFormat.format(aggregatedStats.framesDropped) : 'N/A'}</br>`;
            statsText += `Latency (ms): ${aggregatedStats.hasOwnProperty('currentRoundTripTime') ? numberFormat.format(aggregatedStats.currentRoundTripTime * 1000) : 'N/A'}</br>`;

            //console.log(statsText);
        };

        webRtcPlayerObj.aggregateStats(1 * 1000 /*Check every 1 second*/);
    }

    function requestQualityControl() {
        sendInputData(new Uint8Array([MessageType.RequestQualityControl]).buffer);
    }

    let unquantizeAndDenormalizeUnsigned = undefined;

    function setupNormalizeAndQuantize() {
        let playerElement = self.playerElement;
        let videoElement = playerElement.getElementsByTagName("video");
        let print_inputs = self.print_inputs;
        if (playerElement && videoElement.length > 0) {
            let playerAspectRatio = playerElement.clientHeight / playerElement.clientWidth;
            let videoAspectRatio = videoElement[0].videoHeight / videoElement[0].videoWidth;
            if (playerAspectRatio > videoAspectRatio) {
                if (print_inputs) {
                    console.log('Setup Normalize and Quantize for playerAspectRatio > videoAspectRatio');
                }
                let ratio = playerAspectRatio / videoAspectRatio;
                // Unsigned.
                normalizeAndQuantizeUnsigned = (x, y) =>
                {
                    let normalizedX = x / playerElement.clientWidth;
                    let normalizedY = ratio * (y / playerElement.clientHeight - 0.5) + 0.5;
                    if (normalizedX < 0.0 || normalizedX > 1.0 || normalizedY < 0.0 || normalizedY > 1.0) {
                        return {
                            inRange: false,
                            x: 65535,
                            y: 65535
                        };
                    } else {
                        return {
                            inRange: true,
                            x: normalizedX * 65536,
                            y: normalizedY * 65536
                        };
                    }
                }
                ;
                unquantizeAndDenormalizeUnsigned = (x, y) =>
                {
                    let normalizedX = x / 65536;
                    let normalizedY = (y / 65536 - 0.5) / ratio + 0.5;
                    return {
                        x: normalizedX * playerElement.clientWidth,
                        y: normalizedY * playerElement.clientHeight
                    };
                }
                ;
                // Signed.
                normalizeAndQuantizeSigned = (x, y) =>
                {
                    let normalizedX = x / (0.5 * playerElement.clientWidth);
                    let normalizedY = (ratio * y) / (0.5 * playerElement.clientHeight);
                    return {
                        x: normalizedX * 32767,
                        y: normalizedY * 32767
                    };
                }
                ;
            } else {
                if (print_inputs) {
                    console.log('Setup Normalize and Quantize for playerAspectRatio <= videoAspectRatio');
                }
                let ratio = videoAspectRatio / playerAspectRatio;
                // Unsigned.
                normalizeAndQuantizeUnsigned = (x, y) =>
                {
                    let normalizedX = ratio * (x / playerElement.clientWidth - 0.5) + 0.5;
                    let normalizedY = y / playerElement.clientHeight;
                    if (normalizedX < 0.0 || normalizedX > 1.0 || normalizedY < 0.0 || normalizedY > 1.0) {
                        return {
                            inRange: false,
                            x: 65535,
                            y: 65535
                        };
                    } else {
                        return {
                            inRange: true,
                            x: normalizedX * 65536,
                            y: normalizedY * 65536
                        };
                    }
                }
                ;
                unquantizeAndDenormalizeUnsigned = (x, y) =>
                {
                    let normalizedX = (x / 65536 - 0.5) / ratio + 0.5;
                    let normalizedY = y / 65536;
                    return {
                        x: normalizedX * playerElement.clientWidth,
                        y: normalizedY * playerElement.clientHeight
                    };
                }
                ;
                // Signed.
                normalizeAndQuantizeSigned = (x, y) =>
                {
                    let normalizedX = (ratio * x) / (0.5 * playerElement.clientWidth);
                    let normalizedY = y / (0.5 * playerElement.clientHeight);
                    return {
                        x: normalizedX * 32767,
                        y: normalizedY * 32767
                    };
                }
                ;
            }
        }
    }

    let styleWidth;
    let styleHeight;
    let styleTop;
    let styleLeft;
    let styleCursor = 'default';
    let styleAdditional;

    function resizePlayerStyleToFillWindow() {
        let playerElement = self.playerElement;
        let videoElement = playerElement.getElementsByTagName("VIDEO");
        // Fill the player display in window, keeping picture's aspect ratio.
        let windowAspectRatio = window.innerHeight / window.innerWidth;
        let playerAspectRatio = playerElement.clientHeight / playerElement.clientWidth;
        // We want to keep the video ratio correct for the video stream
        let videoAspectRatio = videoElement.videoHeight / videoElement.videoWidth;
        if (isNaN(videoAspectRatio)) {
            //Video is not initialised yet so set playerElement to size of window
            styleWidth = window.innerWidth;
            styleHeight = window.innerHeight;
            styleTop = 0;
            styleLeft = 0;
            playerElement.style = "top: " + styleTop + "px; left: " + styleLeft + "px; width: " + styleWidth + "px; height: " + styleHeight + "px; cursor: " + styleCursor + "; " + styleAdditional;
        } else if (windowAspectRatio < playerAspectRatio) {
            // Window height is the constraining factor so to keep aspect ratio change width appropriately
            styleWidth = Math.floor(window.innerHeight / videoAspectRatio);
            styleHeight = window.innerHeight;
            styleTop = 0;
            styleLeft = Math.floor((window.innerWidth - styleWidth) * 0.5);
            //Video is now 100% of the playerElement, so set the playerElement style
            playerElement.style = "top: " + styleTop + "px; left: " + styleLeft + "px; width: " + styleWidth + "px; height: " + styleHeight + "px; cursor: " + styleCursor + "; " + styleAdditional;
        } else {
            // Window width is the constraining factor so to keep aspect ratio change height appropriately
            styleWidth = window.innerWidth;
            styleHeight = Math.floor(window.innerWidth * videoAspectRatio);
            styleTop = Math.floor((window.innerHeight - styleHeight) * 0.5);
            styleLeft = 0;
            //Video is now 100% of the playerElement, so set the playerElement style
            playerElement.style = "top: " + styleTop + "px; left: " + styleLeft + "px; width: " + styleWidth + "px; height: " + styleHeight + "px; cursor: " + styleCursor + "; " + styleAdditional;
        }
    }
}

function onMousewheel(e) {
    let delta = e.wheelDelta;
    let x = e.offsetX;
    let y = e.offsetY;
    let Data = new DataView(new ArrayBuffer(7));
    Data.setUint8(0, MessageType.MouseWheel);
    Data.setInt16(1, delta, true);
    Data.setUint16(3, x, true);
    Data.setUint16(5, y, true);
    sendInputData(Data.buffer);
}

function onMouseDown(e) {
    let button = e.button;
    let x = e.offsetX;
    let y = e.offsetY;
    let Data = new DataView(new ArrayBuffer(6));
    Data.setUint8(0, MessageType.MouseDown);
    Data.setUint8(1, button);
    Data.setUint16(2, x, true);
    Data.setUint16(4, y, true);
    sendInputData(Data.buffer);
    mouseDownBtnsFlag[e.button] = {button:e.button,offsetX:x,offsetY:y};
    if (selfRender.options.pick_mouse === button && selfRender.pickable && selfRender.onPick){
        selfRender.call(FUNC_CODE.Object.pick,{},selfRender.onPick);
    }
    //console.log('on mouse down');
}

function onMouseMove(e) {
    if (!normalizeAndQuantizeSigned) return;
    let x = e.offsetX;
    let y = e.offsetY;
    let deltaX = e.movementX;
    let deltaY = e.movementY;
    let delta = normalizeAndQuantizeSigned(deltaX, deltaY);
    let Data = new DataView(new ArrayBuffer(9));
    Data.setUint8(0, MessageType.MouseMove);
    Data.setUint16(1, x, true);
    Data.setUint16(3, y, true);
    Data.setInt16(5, delta.x, true);
    Data.setInt16(7, delta.y, true);
    sendInputData(Data.buffer);
}

function onMouseUp(e) {
    let button = e.button;
    let x = e.offsetX;
    let y = e.offsetY;
    let Data = new DataView(new ArrayBuffer(6));
    Data.setUint8(0, MessageType.MouseUp);
    Data.setUint8(1, button);
    Data.setUint16(2, x, true);
    Data.setUint16(4, y, true);
    sendInputData(Data.buffer);
    delete mouseDownBtnsFlag[e.button];
    //console.log('on mouse up',mouseDownBtnsFlag);
}

function onPlayerBlur(){
    //console.log('on blur player...');
    let keys = Object.keys(mouseDownBtnsFlag);
    for (let i=0;i<keys.length;i++){
        onMouseUp(mouseDownBtnsFlag[keys[i]]);
    }
}

function onKeydown(e) {
    let k = getKeyCode(e);
    if (k === SpecialKeyCodes.F11) return;
    sendInputData(new Uint8Array([MessageType.KeyDown, k, e.repeat]).buffer);
}

function onKeypress(e) {
    let k = getKeyCode(e);
    if (k === SpecialKeyCodes.F11) return;
    let data = new DataView(new ArrayBuffer(3));
    data.setUint8(0, MessageType.KeyPress);
    data.setUint16(1, k, true);
    sendInputData(data.buffer);
}

function onKeyup(e) {
    let k = getKeyCode(e);
    if (k === SpecialKeyCodes.F11) return;
    sendInputData(new Uint8Array([MessageType.KeyUp, k]).buffer);
}